/*
  Name: level.CPP
  Copyright: Nou
  Author: Julian Williams
  Date: 4/18/10
  Description: Handels the level routines that are used to 
				control the envirnment 
*/


#include <iostream>
#include <fstream>
#include <assert.h>
#include <physfs.h>

#include <SFML/Graphics.hpp> // Include the allegro header file.


#include "app.h"
#include "helper.h"
#include "SFMLMappy.h"
#include "levels.h"
#include "cfg_reader.h"
#include "Camera.h"
#include "text.h"
#include "path_finder.h"
#include "path_component.h"
#include "physics_world.h"
#include "script_engine.h"



using namespace std;

//define Map instance

/* scripting Interface */
void Map_load_next( c_Map * map)
{
	map->Load_Next();
}

void Map_load_prev( c_Map * map)
{
	map->Load_Previous();
}

void Map_load_number( c_Map * map, int mapnum)
{
	map->Load(mapnum);
}

void Map_reset( c_Map * map)
{
	map->Reset();
}

float Map_get_gravity(const c_Map * map)
{
	return map->gravity;
}

void Map_set_gravity( c_Map * map, float grav)
{
	map->gravity = grav;
}

int Map_get_blocksizeX( c_Map * map)
{
	return map->Get_BlockSize().x;
}

int Map_get_blocksizeY( c_Map * map)
{
	return map->Get_BlockSize().y;
}




bool c_Map::Register_With_ScriptEngine()
{
	int nRet = 0;
	nRet = aScriptEngine.GetEngine()->RegisterObjectType("c_Map", 0, asOBJ_REF | asOBJ_NOHANDLE); 
	assert( nRet >= 0 );

	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "void load_next()", asMETHOD(c_Map, Load_Next), asCALL_THISCALL);
	assert( nRet >= 0 );
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "void load_prev()", asFUNCTION(Map_load_prev), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "void load_number(int)", asFUNCTION(Map_load_number), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "void reset()", asFUNCTION(Map_reset), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "float get_gravity()", asFUNCTION(Map_get_gravity), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "void set_gravity(float)", asFUNCTION(Map_set_gravity), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "int get_blockwidth( )", asFUNCTION(Map_get_blocksizeX), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("c_Map", "int get_blockheight( )", asFUNCTION(Map_get_blocksizeY), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );
	
	//Register properties
	nRet = aScriptEngine.GetEngine()->RegisterObjectProperty("c_Map", "vector2 pathStart", offsetof(c_Map, pathStart)); 
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterObjectProperty("c_Map", "vector2 pathEnd", offsetof(c_Map, pathEnd)); 
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterGlobalProperty("c_Map Map", &Map); 
	assert( nRet >= 0 );

	

// Angelscript class registration
	/*
r = engine->RegisterObjectType("c_Map", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );
r = engine->RegisterObjectMethod("c_Map", "int get_variable(int index)", asFUNCTION(Proxy_getvariable), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectMethod("c_Map", "void set_variable(const string &in, int index)", asFUNCTION(Proxy_setvariable), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterGlobalProperty("MyClass mc", &mc); assert( r >= 0 );

*/	
	 if(nRet < 0)
	 {
		 cout << "Register Class Result: " << nRet << endl ;
		 return false;
	 }

	return true;
}//EoF




c_Map Map;

c_Map::c_Map()
{
	current_map = 0;
}


/* Set's up the Map list */
/*************************/
bool c_Map::Setup_List(string cfg_file)
{
	 cout << "****************************************************************\n";
	cout << "-Setting Map list up-\n\n";

 
	//Get level directory
	string List_loc = cfg_file + "List.cfg";
	string item_name;


	//Open up the list List file
	ifstream List_file(List_loc.c_str());
    
	//if file isn't open, return false
	if(!List_file.is_open()) { return false; }

	do 
	{
		//Read in every line
		getline( List_file, item_name);

		if(item_name == "") { break; }

		List.push_back(item_name);
		cout << "Item '" << item_name <<"' added" << endl;

	} while( !List_file.eof() );

	//save the number of maps
	max_count = List.size()-1;

	//Close file
	List_file.close();

	cout << "List Setup complete, total list count: " << max_count << " Recorded. \n";
	cout << "****************************************************************\n\n";
	return true;
}//end ov function



/* Effects Manager */
/******************
void c_Map::effects_manager()
{
     /* I should move this 
         switch(FX)
         {
                case FX_RAIN:
                     FX_rain(get_buffer(), 0);
                     
                    break;
         }
}//end ov function
*/





/* Getter's/Setter's for map number */
/************************************/
void c_Map::Set_Current(int mapNum)
{
	//bounds check the current map

	  current_map = mapNum;

	  if(current_map > max_count)
	  {
		  current_map = 0;
	  }
	  else if(current_map < 0)
	  {
		  current_map = max_count;
	  }
}//EoF


int c_Map::Get_Current()
{
    return current_map;
}//EoF







/* Read in info about the map */
/******************************/
/*Read level stats from file and save it to level Object*/     
bool c_Map::Get_Info(string datafile_loc)
{  
	 string fg_loc		= App::getInstance().Resources_Dir["foregrounds"];
     string bg_loc		= App::getInstance().Resources_Dir["backgrounds"];
     string music_loc	= App::getInstance().Resources_Dir["soundtrack"];

		cout << "Foregrounds from: " << fg_loc << endl;
        cout << "Backgrounds from: " << bg_loc << endl;
		cout << "Music from: " << music_loc << endl;

     	//open up the configuration file for reading

		cfg_reader cfg(datafile_loc, false);

		//Get gravity
		 savedGravity.x = cfg.Get_Info("Info:gravity_x", 0);
		 savedGravity.y = cfg.Get_Info("Info:gravity_y", 0);
		// cout << "Gravity: " << savedGravity << endl;
		physicsManager.world->SetGravity( savedGravity );

//Load background
/****************/
		 int background_count = cfg.Get_Info("Backgrounds:background_count", 0);
 
		 for(int i = 0; i < background_count; i++)
		 {
			 //Create the name to look up
			 string getName = Text::Create_TextWithArgs("Backgrounds:name_%d", i);
			 string getSpeed = Text::Create_TextWithArgs("Backgrounds:speed_%d", i);

		//	 string getNamem = string("Backgrounds:b_0");

			 cout << getSpeed << ", " << getName << endl;

			 //Load Background name, and scroll speed
     		 string bg_name = cfg.Get_Info( getName.c_str(), "default.png" );
			 double scroll_speed =  cfg.Get_Info( getSpeed.c_str(), 0.5);

			 //if loading has failed, skip it, and don't load it
			 if(bg_name == "default.png") { continue; }


		//Add Name to its location
			string nbg_loc = bg_loc + bg_name;
			cout << nbg_loc << endl;

			//create new parallax background object
			Parallax p(scroll_speed, nbg_loc);

			//add to background list
			Backgrounds.push_back(p);
		 }//end ov for 



//Load Foreground
		 int foreground_count = cfg.Get_Info("Foregrounds:foreground_count", 0);
 
		 for(int i = 0; i < foreground_count; i++)
		 {
			 //Create the name to look up
			 string getName = Text::Create_TextWithArgs("Foregrounds:name_%d", i);
			 string getSpeed = Text::Create_TextWithArgs("Foregrounds:speed_%d", i);

		//	 string getNamem = string("Backgrounds:b_0");

			 cout << getSpeed << ", " << getName << endl;

			 //Load Background name, and scroll speed
     		 string fg_name = cfg.Get_Info( getName.c_str(), "default.png" );
			 double scroll_speed =  cfg.Get_Info( getSpeed.c_str(), 1.5);

			 //if loading has failed, skip it, and don't load it
			 if(fg_name == "default.png") { continue; }


		//Add Name to its location
			string nfg_loc = fg_loc + fg_name;
			cout << nfg_loc << endl;

			//create new parallax background object
			Parallax p(scroll_speed, nfg_loc);

			//add to background list
			Foregrounds.push_back(p);
		 }//end ov for 
     
     
     
//Load Music
/************/
     string music_name = cfg.Get_Info("Info:music", "<none>");
     //Add name to its location
	 music_loc += music_name;

     
     if(music_name == "<none>")
     {
                 cout << "No music found at - " << music_loc << endl;
     }else{
           //Load Music
		 cout << "Loading Music from: " << music_loc << endl;

		 if(!App::getInstance().Music.OpenFromFile(music_loc))
		 {
				 cout << "Couldn't Open: " << music_loc << endl;
		 }

		 App::getInstance().Music.SetVolume(30.0f);
		 App::getInstance().Music.SetLoop(true);
	

		// App::getInstance().Music.Play();
				   
     }
     

	return true;
}//End ov function





/* Base Map Loader */
/*******************/
bool c_Map::Load()
{

	  Backgrounds.reserve(2);

      
      string mapFmp_location;
      string mapInfo_location;
      
	  
      string map_dir = App::getInstance().Resources_Dir["levels"] + List[current_map] + "/";
      
      
      //fade out
     // screen_fade_out(15);
      


      //bounds check map number
      if(current_map > max_count) { current_map = 0; }    
	  if(current_map < 0) { current_map = max_count; }


	  //Set up locations of files to read into the engine
	  mapFmp_location  = map_dir + string("stage.FMP");
	  mapInfo_location = map_dir + string("info.cfg");

	  //Print out location to console
      cout << ">Loading Level[" << current_map << "] - '" << List[current_map] <<  "' From location - " << mapFmp_location << endl;
      
      
      if(mapFmp_location == map_dir + "(null)/stage.fmp") 
      {
                         cout << "\nCannot load map " << List[current_map];
                          return false;
                        //  Game_State = MENU;
      }else{
    

			//Load data
			 if(!Get_Info(mapInfo_location))
			 {
				 cout << "Map info was not loaded correcly...\n";
				 return false;
			 }
     
			 //Load Map 

			 
			 //I WILL rid myself ov mappy Sooner or later...
			if (!LoadMap(mapFmp_location.c_str())) 
			 //if (!DecodeMap( myBuf ))
			 {
				 cout << "\nFAILED to load map....sorry :0" << endl;
				 return false;
               //   g_error("Sorry, End of the Demo :( \n Please, help me pay for school so I can do this for a living!!!");
			 }else{
					cout << "-Current Map: " << current_map << "\n\n";
			 }	
      
			//speed up block look ups
			 GenerateMapYLookup();

			 //Convert our variables from mappy's
			 size.x = GetMapWidth();
		  	 size.y = GetMapHeight();
      
			 tile_dimensions.x = GetMapBlockWidth();
			 tile_dimensions.y = GetMapBlockHeight();
            
            
                           
		ChangeMapLayer(1);
      
		//Loop through the map and set landscape properties
			Pather.Setup(size.x, size.y);
			PathComponent::SetGridSize( b2Vec2(tile_dimensions.x, tile_dimensions.y) );

			//Setup Physics boxes for tiles
			b2BodyDef bodyDef;
			bodyDef.type = b2_staticBody;
			
			b2CircleShape shapeDef;
			//shapeDef.SetAsBox(GRID_SIZE/2*METERS_PER_PIXEL, GRID_SIZE/2*METERS_PER_PIXEL);
			shapeDef.m_radius = tile_dimensions.x/2*METERS_PER_PIXEL;

			b2FixtureDef fixtureDef;
			fixtureDef.shape = &shapeDef;
			
			fixtureDef.filter.categoryBits = 0x0004;
			fixtureDef.friction = 0;
			b2Body *body;

		//	body->SetUserData((void*)(collisionEntity));


			sf::Shape test_rec = sf::Shape::Circle( 0, 0, tile_dimensions.x/2, sf::Color::Yellow, 1, sf::Color::Black);
						
				
			//Set up Grid
			for(int y = 0; y < size.y; y++)
			{
				for(int x = 0; x < size.x; x++)
				{
					
					if(GetMapBlock(x, y)->user1 == Triggers::CREEP_SPAWN)
					{
						pathStart.x = x * tile_dimensions.x;
						pathStart.y = y * tile_dimensions.y;
						
						test_rec.SetColor(sf::Color(100,200,0, 100));
						test_rec.SetPosition(sf::Vector2f((x*tile_dimensions.x)+tile_dimensions.x/2, (y*tile_dimensions.y)+tile_dimensions.y/2));
						shape_list.push_back(test_rec);


						Pather.Set_Passable(x, y, Triggers::CREEP_SPAWN);
					}
					else if(GetMapBlock(x, y)->user1 == Triggers::CREEP_EXIT)
					{

						pathEnd.x = x * tile_dimensions.x;
						pathEnd.y = y * tile_dimensions.y;
						
						test_rec.SetColor(sf::Color::Red);
						test_rec.SetPosition(sf::Vector2f((x*tile_dimensions.x)+tile_dimensions.x/2, (y*tile_dimensions.y)+tile_dimensions.y/2));
						shape_list.push_back(test_rec);


						Pather.Set_Passable(x, y, CREEP_EXIT);
					}
					else if(GetMapBlock(x, y)->user1 == Triggers::NOT_BUILDABLE_OR_WALKABLE)
					{
						b2Vec2 location(((x*tile_dimensions.x)+tile_dimensions.x/2)*METERS_PER_PIXEL,  ((y*tile_dimensions.y)+tile_dimensions.y/2)*METERS_PER_PIXEL);

						bodyDef.position = location;

						mesh_list.push_back(body);
						mesh_list.back() = physicsManager.world->CreateBody(&bodyDef);
						mesh_list.back()->CreateFixture(&fixtureDef);


						test_rec.SetColor(sf::Color(255,255,255, 100));
						test_rec.SetPosition(sf::Vector2f((x*tile_dimensions.x)+tile_dimensions.x/2, (y*tile_dimensions.y)+tile_dimensions.y/2));
						shape_list.push_back(test_rec);

						Pather.Set_Passable(x, y, Triggers::NOT_BUILDABLE_OR_WALKABLE);

					}
					else if(GetMapBlock(x, y)->user1 == Triggers::OPEN)
					{
						test_rec.SetColor(sf::Color(0,200,10, 100));
						test_rec.SetPosition(sf::Vector2f((x*tile_dimensions.x)+tile_dimensions.x/2, (y*tile_dimensions.x)+tile_dimensions.y/2));
						shape_list.push_back(test_rec);

						Pather.Set_Passable(x, y, Triggers::OPEN);

					}

				//	Pather.Set_Passable(x, y, GetMapBlock(x, y)->user1);
					
				//	cout << "( " << x << ", " << y << " ) = " << Pather.Get_Passable(x, y) << endl;

				}
			}
			
			//player.Reset(); //player spawn and reset vars
			//enemy_spawn(); //Enemy spawns 
               
			ChangeMapLayer(0);  
       // MappyLevel.SetMapDimensions(App::getInstance().Get_RenderWindow()->GetWidth(), App::getInstance().Get_RenderWindow()->GetHeight());
      
		//checkpoint.save_position(player.px, player.py, x_start, y_start); //clear checkpoints
      
      }//end ov real map    

	  SetMapStartPosition(-1, -1);
	  SetMapDimensions(Get_PixelSize().x - 1, Get_PixelSize().y -1);
	 return true;
}//End ov function





/* Load Map via Number */
/***********************/
bool c_Map::Load(int mapNum)
{
     Set_Current(mapNum);
     return Load();
}


/* Load Next Map */
/*****************/
bool c_Map::Load_Next()
{
     Set_Current(current_map++);
     return Load();
}


/* Load Previous Map */
bool c_Map::Load_Previous()
{
     Set_Current(current_map--);
     return Load();
}




/* Reset current Map */
/*********************/
void c_Map::Reset()
{
	//Reset backgrounds
	for(int i = 0; i < Backgrounds.size(); i++)
	{
		Backgrounds[i].Reset();
	}

	/*
     screen_fade_out(15);
     
     player.state = ground;
     player.px = checkpoint.set_x();
     player.py = checkpoint.set_y();
     
     player.x_vel = 0;
     player.y_vel = 0;
     
     x_scroll = checkpoint.set_scroll_x();
     y_scroll = checkpoint.set_scroll_y();
	 */
}//end ov function




//Update logic for map
void c_Map::Update_Logic()
{
	//update maps animations
	 UpdateMapAnims();


	 //Update Gravity
	// Map.gravity = Map.savedGravity * App::getInstance().Get_RenderWindow()->GetFrameTime();

//UPdate Background logic
	 for(int i = 0; i < Backgrounds.size(); i++)
	 {
		 Backgrounds[i].Update_Logic();
	 }

	 for(int i = 0; i < Foregrounds.size(); i++)
	 {
		 Foregrounds[i].Update_Logic();
	 }
	 
}//EoF




/* Draw the Map Background */
/***************************/
void c_Map::Draw_Backgrounds()
{

	 for(int i = 0; i < Backgrounds.size(); i++)
	 {
		Backgrounds[i].Draw();					
	 }
}//EoF



/* Draw the Map Foregrounds */
/***************************/
void c_Map::Draw_Foregrounds()
{

	 for(int i = 0; i < Foregrounds.size(); i++)
	 {
		Foregrounds[i].Draw();					
	 }
}//EoF



/* Draw Map with unique layer */
/******************************/
void c_Map::Draw(int layer)
{
//blit the Background
	  //Change_Layers(0);

	
	
      switch(layer)
      {
                   case 0:
					    
                     //  blit(Images.background, get_buffer(), 0, 0, 0, 0, Images.background->w, Images.background->h);
                        
					    DrawMapBG (*App::getInstance().Get_RenderWindow(),  0, 0);     //Draw background later   
                       
                        break;
                        
                   default:
                           
                        DrawMapFG(*App::getInstance().Get_RenderWindow(), 0,  0, layer-1);     //Draw foreground later
      }//end ov switch  
	  

	  
}//End ov function



void c_Map::Destroy()
{
              // MappyLevel.FreeMapMem();
}


//Change layers
void c_Map::Change_Layers(const int layer)
{
	ChangeMapLayer(layer);
}









